#ifndef __RAYCASTEDGLOBE__H
#define __RAYCASTEDGLOBE__H

#include "../Core/Utils.h"
#include "../Core/Ellipsoid.h"
//#include "../Render/GLSLShader.h"
//#include "../Render/RenderableObject.h"
#include "../Core/IDisposable.h"
#include "../Core/BoxTessellator.h"

#include "../Renderer/DrawCommand.h"
#include "../Renderer/Uniform.h"
#include "../Renderer/RenderState.h"
#include "../Renderer/VertexArray.h"
#include "../Renderer/Context.h"
#include "../Renderer/Device.h"
#include "../Renderer/SceneState.h"
#include "../Renderer/Texture2D.h"
#include "../Renderer/TextureSamplers.h"

using namespace Core;
using namespace Renderer;

namespace Scene{

	class RayCastedGlobe : public Core::IDisposable
	{
	public:
		RayCastedGlobe(Context* context)
		{
			_renderState = new RenderState();
			_renderState->FacetCulling.Face = CullFace::FrontAndBack;
			ShaderProgram* sp = Device::GetInstance()->CreateShaderProgramFromFile("shaders/RayCastedGlobe.vert", "shaders/RayCastedGlobe.frag");

			_cameraEyeSquared = (TemplateUniform<Vector3F>*)sp->GetUniforms()->Get("u_cameraEyeSquared");
			//_useAverageDepth = (TemplateUniform<bool>*)sp->GetUniforms()->Get("u_useAverageDepth");

			_drawState = new DrawCommand(_renderState, sp, 0);

			_shape = Ellipsoid::ScaledWgs84;
			_shade = true;
			_showGlobe = true;
			
		}
		~RayCastedGlobe()
		{

		}

		void Clean(Context* context)
		{
			if (_dirty)
			{
				if (_va)
				{
					_va->Destroy();
					delete _va;
					_va = 0;
					
					_drawState->SetVertexArray(0);
				}
				auto mesh = BoxTessellator::Compute(Vec3Multiply(_shape->getRadii(),2));
				_va = context->CreateVertexArray(mesh, _drawState->GetShaderProgram()->GetVertexAttributes(), BufferHint::StaticDraw);
				_drawState->SetVertexArray(_va);
				_primitiveType = mesh->GetPrimitiveType();
				_renderState->FacetCulling.FrontFaceWindingOrder = mesh->GetFrontFaceWindingOrder();

				auto u_globeOneOverRadiiSquared = ((TemplateUniform<Vector3F>*)_drawState->GetShaderProgram()->GetUniforms()->Get("u_globeOneOverRadiiSquared"));
				u_globeOneOverRadiiSquared->SetValue(Vector3F(_shape->getOneOverRadiiSquared()));


				_dirty = false;
			}
		}

		void Render(Context* context, SceneState* sceneState)
		{
			Clean(context);

			if (_showGlobe)
			{
				Vector3D eye = sceneState->GetCamera()->Eye();
				Vector3F cameraEyeSquared = Vector3F(Vec3MultiplyComponents(eye, eye));

				if (_shade)
				{
					auto textureUnit = context->GetTextureUnits()->GetTextureUnitAt(0);
					auto textureSampler = Device::GetInstance()->GetTextureSamplers()->GetLinearClamp();
					textureUnit->SetTexture(_texture);
					textureUnit->SetTextureSampler(textureSampler);
					
					_cameraEyeSquared->SetValue(cameraEyeSquared);
					//_useAverageDepth->SetValue(false);

					context->Draw(_primitiveType, _drawState, sceneState);
				}
				else
				{

				}
			}
		}

		bool GetUseAverageDepth() const { return _useAverageDepth->GetValue(); }
		void SetUseAverageDepth(bool v)
		{
			_useAverageDepth->SetValue(v);
		}

		Ellipsoid* GetShape() const { return _shape; }
		void SetShape(Ellipsoid* shape)
		{
			_dirty = true;
			_shape = shape;
		}

		bool GetShade() const { return _shade; }
		void SetShade(bool v) { _shade = v; }

		bool GetShowGlobe() const { return _showGlobe; }
		void SetShowGlobe(bool v) { _showGlobe = v; }

		Texture2D* GetTexture() const { return _texture; }
		void SetTexture(Texture2D* texture) { _texture = texture; }

		void Destroy() override
		{
			_drawState->GetShaderProgram()->Destroy();
			if (_va)
			{
				_va->Destroy();
			}
		}

	private:
		DrawCommand* _drawState;
		TemplateUniform<Vector3F>* _cameraEyeSquared;
		TemplateUniform<bool>* _useAverageDepth;

		RenderState* _renderState;
		VertexArray* _va;
		PrimitiveType _primitiveType;
		Ellipsoid* _shape;
		bool _dirty;

		bool _shade;
		bool _showGlobe;

		Texture2D* _texture;

	};

}

#endif